package com.course.web.component.custom;

import com.course.domain.enums.TaskType;
import com.course.domain.model.TaskGroup;
import com.course.web.biz.WebCourseBiz;
import com.course.web.biz.WebTaskBiz;
import com.course.web.component.base.BaseEdit;
import com.course.web.component.base.BaseGrid;
import com.course.web.component.base.BaseList;
import com.course.web.component.base.EditGrid;
import com.course.web.model.*;
import com.course.web.view.CourseListView;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;
import com.vaadin.ui.*;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by Justin on 2017/6/11.
 */
@SpringComponent
@ViewScope
public class CoursePlanGrid extends EditGrid<CourseWeekVM> {

    private static final String[] WEEK = {"周一", "周二", "周三", "周四", "周五", "周六", "周日"};

    private final WebCourseBiz courseBiz;
    private final WebTaskBiz taskBiz;

    private final AudioEdit audioEdit;
    private final VideoEdit videoEdit;
    private final StoryEdit storyEdit;
    private final LearnEdit learnEdit;
    private final ClassEdit classEdit;
    private final EventEdit eventEdit;
    private final ReportWin reportWin;

    private Button lastWeekBtn;
    private Button nextWeekBtn;
    private TextField thisWeekTxt;

    private CourseInfoVM courseInfo;
    private int courseId;
    private int weekIdx;

    public CoursePlanGrid(WebCourseBiz courseBiz, WebTaskBiz taskBiz) {
        super(CourseWeekVM.class);
        this.courseBiz = courseBiz;
        this.taskBiz = taskBiz;

        this.audioEdit = new AudioEdit();
        this.videoEdit = new VideoEdit();
        this.storyEdit = new StoryEdit();
        this.learnEdit = new LearnEdit();
        this.classEdit = new ClassEdit();
        this.eventEdit = new EventEdit();
        this.reportWin = new ReportWin();
    }

    @Override
    protected void doInitMenu(Layout menu) {
        this.thisWeekTxt = new TextField();
        this.lastWeekBtn = new Button("上一周");
        this.nextWeekBtn = new Button("下一周");
        Button backBtn = new Button("返回");
        Button textBtn = new Button("报告");

        menu.addComponent(backBtn);
        menu.addComponent(lastWeekBtn);
        menu.addComponent(thisWeekTxt);
        menu.addComponent(nextWeekBtn);
        menu.addComponent(textBtn);

        thisWeekTxt.setEnabled(false);
        lastWeekBtn.addClickListener(event -> toWeek(weekIdx - 1));
        nextWeekBtn.addClickListener(event -> toWeek(weekIdx + 1));
        backBtn.addClickListener(event -> getUI().getNavigator().navigateTo(CourseListView.NAME));
        textBtn.addClickListener(event -> {
            reportWin.enter(courseBiz.listWeek(courseId, weekIdx), taskBiz.findGroup(courseId));
            getUI().addWindow(reportWin);
        });
    }

    @Override
    protected void doInitGrid(BaseGrid<CourseWeekVM> grid) {
        grid.addColumn(CourseWeekVM::getPartIdx, "序号");
        grid.addColumn(CourseWeekVM::getWorkday, "平日时段");
        for (int i = 0; i < 7; i++) {
            int weekDay = i + 1;
            String id = String.valueOf(weekDay);
            grid.addColumn(vm -> toTitle(vm, weekDay), WEEK[i], id);
        }
        grid.addColumn(CourseWeekVM::getWeekend, "周末时段");
        grid.setHandler(this::editShow);
    }

    private String toTitle(CourseWeekVM vm, int weekDay) {
        TaskType type = vm.getItem(weekDay).getTaskType();
        if (type.equals(TaskType.NONE)) {
            return "";
        }
        return type.getTitle() + "(" + vm.getItem(weekDay).getTaskId() + ")";
    }

    private void editShow(CourseWeekVM week, String id) {
        int weekDay = NumberUtils.toInt(id);
        CourseItemVM item = week.getItem(weekDay);
        TaskType type = item.getTaskType();
        switch (type) {
            case LEARN:
                learnEdit.enter(week, item);
                learnEdit.show(getUI(), item);
                break;
            case CLASS:
                classEdit.setHandler(data -> saveItem(week, item));
                classEdit.show(getUI(), item);
                break;
            case EVENT:
                eventEdit.setHandler(data -> saveItem(week, item));
                eventEdit.show(getUI(), item);
                break;
        }
    }

    private void saveItem(CourseWeekVM week, CourseItemVM item) {
        if (item.getTaskId() == 0) {
            return;
        }
        if (item.getId() == 0) {
            courseBiz.createItem(item);
        } else {
            courseBiz.updateItem(item);
        }
        update(week);
    }

    private void toWeek(int weekIdx) {
        if (weekIdx < 0 || weekIdx > courseInfo.getWeeks()) {
            return;
        }
        this.weekIdx = weekIdx;
        thisWeekTxt.setValue(String.valueOf(weekIdx));
        lastWeekBtn.setEnabled(weekIdx > 1);
        nextWeekBtn.setEnabled(weekIdx < courseInfo.getWeeks());
        init(courseBiz.listWeek(courseId, weekIdx));
    }

    public void enter(int courseId) {
        this.courseId = courseId;
        this.courseInfo = courseBiz.loadInfo(courseId);
        List<TaskAudioVM> audioList = taskBiz.findAudio(courseId);
        List<TaskVideoVM> videoList = taskBiz.findVideo(courseId);
        List<TaskStoryVM> storyList = taskBiz.findStory(courseId);
        List<TaskGroupVM> groupList = taskBiz.findGroup(courseId);
        List<TaskClassVM> classList = taskBiz.findClass(courseId);
        List<TaskEventVM> eventList = taskBiz.findEvent(courseId);
        audioEdit.enter(audioList);
        videoEdit.enter(videoList);
        storyEdit.enter(storyList);
        learnEdit.enter(groupList);
        classEdit.enter(classList);
        eventEdit.enter(eventList);
        reportWin.enter(audioList, videoList, storyList, classList, eventList);
        toWeek(1);
    }

    private class LearnEdit extends BaseEdit<CourseItemVM> {
        private final BaseGrid<TaskGroupVM> groupGrid;

        private Map<Integer, List<TaskGroupVM>> itemListMap;
        private List<TaskGroupVM> updateList;
        private List<TaskGroupVM> deleteList;
        private List<TaskGroupVM> copiedList;
        private CourseItemVM current;
        private int taskId;

        LearnEdit() {
            super(CourseItemVM.class);
            this.groupGrid = new BaseGrid<>();
            this.deleteList = Lists.newArrayList();
            this.copiedList = Lists.newArrayList();

            Layout root = new VerticalLayout();
            Layout menu = new HorizontalLayout();
            Button audio = new Button("添加音频");
            Button video = new Button("添加视频");
            Button story = new Button("添加故事");
            Button copy = new Button("复制");
            Button redo = new Button("粘贴");
            Layout bars = getBars();
            root.addComponent(menu);
            root.addComponent(groupGrid);
            menu.addComponent(audio);
            menu.addComponent(video);
            menu.addComponent(story);
            groupGrid.addColumn(TaskGroupVM::getIdx, "序号");
            groupGrid.addColumn(TaskGroupVM::getTypeName, "类型");
            groupGrid.addColumn(TaskGroupVM::getItemId, "任务ID");
            groupGrid.addButton(this::delete, "删除");
            initEdit(root);
            bars.addComponent(copy);
            bars.addComponent(redo);
            setHandler(data -> save());
            audio.addClickListener(event -> openAudio(audioEdit));
            video.addClickListener(event -> openVideo(videoEdit));
            story.addClickListener(event -> openStory(storyEdit));
            copy.addClickListener(event -> copy());
            redo.addClickListener(event -> redo());
        }

        private void openAudio(AudioEdit edit) {
            edit.setHandler(data -> append(data.getTaskId(), TaskGroup.Type.AUDIO));
            edit.enter();
            edit.show(getUI(), new CourseItemVM(0, 0, 0, 0));
        }

        private void openVideo(VideoEdit edit) {
            edit.setHandler(data -> append(data.getTaskId(), TaskGroup.Type.VIDEO));
            edit.enter();
            edit.show(getUI(), new CourseItemVM(0, 0, 0, 0));
        }

        private void openStory(StoryEdit edit) {
            edit.setHandler(data -> append(data.getTaskId(), TaskGroup.Type.STORY));
            edit.enter();
            edit.show(getUI(), new CourseItemVM(0, 0, 0, 0));
        }

        private void append(int itemId, TaskGroup.Type type) {
            if (itemId > 0) {
                TaskGroupVM item = new TaskGroupVM(courseId);
                item.setTaskId(current.getTaskId());
                item.setItemId(itemId);
                item.setType(type);
                updateList.add(item);
                sort();
            }
        }

        private void delete(TaskGroupVM item) {
            updateList.remove(item);
            deleteList.add(item);
            sort();
        }

        private void save() {
            updateList.forEach(taskBiz::updateGroup);
            deleteList.forEach(taskBiz::deleteGroup);
            itemListMap.put(taskId, updateList);
        }

        private void sort() {
            int idx = 0;
            for (TaskGroupVM vm : updateList) {
                vm.setIdx(++idx);
            }
            groupGrid.setItems(updateList);
        }

        private void copy() {
            copiedList.clear();
            copiedList.addAll(updateList);
        }

        private void redo() {
            for (TaskGroupVM copy : copiedList) {
                TaskGroupVM item = new TaskGroupVM(courseId);
                item.setTaskId(taskId);
                item.setItemId(copy.getItemId());
                item.setType(copy.getType());
                updateList.add(item);
            }
            sort();
        }

        void enter(List<TaskGroupVM> items) {
            this.itemListMap = items.stream().collect(Collectors.groupingBy(TaskGroupVM::getTaskId));
        }

        void enter(CourseWeekVM week, CourseItemVM item) {
            if (item.getTaskId() == 0) {
                TaskLearnVM task = taskBiz.createLearn(new TaskLearnVM(courseId));
                item.setTaskId(task.getId());
                item = courseBiz.createItem(item);
                CoursePlanGrid.this.update(week);
            }
            this.taskId = item.getTaskId();
            this.current = item;
            this.deleteList.clear();
            this.updateList = Lists.newArrayList(itemListMap.computeIfAbsent(taskId, key -> Lists.newArrayList()));
            groupGrid.setItems(updateList);
        }
    }


    private class AudioEdit extends BaseEdit<CourseItemVM> {
        private final BaseList<TaskAudioVM> audioList;

        AudioEdit() {
            super(CourseItemVM.class);
            setCaption("选择音频");
            this.audioList = new BaseList<>();
            audioList.addColumn(TaskAudioVM::getId, "ID");
            audioList.addColumn(TaskAudioVM::getTitle, "音频标题");
            audioList.addColumn(TaskAudioVM::getTimes, "重复次数");
            audioList.addColumn(TaskAudioVM::getSpeed, "播放速度");
            audioList.addColumn(TaskAudioVM::getDetail, "音频详情");
            initEdit(audioList);
            binder().bind(audioList.adapter(), "taskId");
        }

        void enter(List<TaskAudioVM> items) {
            audioList.setItems(items);
        }

        void enter() {
            audioList.deselect();
        }
    }

    private class VideoEdit extends BaseEdit<CourseItemVM> {
        private final BaseList<TaskVideoVM> videoList;

        VideoEdit() {
            super(CourseItemVM.class);
            setCaption("选择视频");
            this.videoList = new BaseList<>();
            videoList.addColumn(TaskVideoVM::getId, "ID");
            videoList.addColumn(TaskVideoVM::getTitle, "视频标题");
            videoList.addColumn(TaskVideoVM::getTimes, "重复次数");
            videoList.addColumn(TaskVideoVM::getDetail, "视频详情");
            initEdit(videoList);
            binder().bind(videoList.adapter(), "taskId");
        }

        void enter(List<TaskVideoVM> items) {
            videoList.setItems(items);
        }

        void enter() {
            videoList.deselect();
        }
    }

    private class StoryEdit extends BaseEdit<CourseItemVM> {
        private final BaseList<TaskStoryVM> storyList;

        StoryEdit() {
            super(CourseItemVM.class);
            setCaption("选择故事");
            this.storyList = new BaseList<>();
            storyList.addColumn(TaskStoryVM::getId, "ID");
            storyList.addColumn(TaskStoryVM::getTitle, "故事标题");
            storyList.addColumn(TaskStoryVM::getTimes, "重复次数");
            storyList.addColumn(TaskStoryVM::getSpeed, "播放速度");
            storyList.addColumn(TaskStoryVM::getDetail, "故事详情");
            initEdit(storyList);
            binder().bind(storyList.adapter(), "taskId");
        }

        void enter(List<TaskStoryVM> items) {
            storyList.setItems(items);
        }

        void enter() {
            storyList.deselect();
        }
    }

    private class ClassEdit extends BaseEdit<CourseItemVM> {
        private final BaseList<TaskClassVM> classList;

        ClassEdit() {
            super(CourseItemVM.class);
            setCaption("选择教学");
            this.classList = new BaseList<>();
            classList.addColumn(TaskClassVM::getId, "ID");
            classList.addColumn(TaskClassVM::getTitle, "教学标题");
            classList.addColumn(TaskClassVM::getMinutes, "教学时长");
            classList.addColumn(TaskClassVM::getType, "老师类型");
            classList.addColumn(TaskClassVM::getDetail, "教学内容");
            initEdit(classList);
            binder().bind(classList.adapter(), "taskId");
        }

        void enter(List<TaskClassVM> items) {
            classList.setItems(items);
        }
    }

    private class EventEdit extends BaseEdit<CourseItemVM> {
        private final BaseList<TaskEventVM> eventList;

        EventEdit() {
            super(CourseItemVM.class);
            setCaption("选择课程");
            this.eventList = new BaseList<>();
            eventList.addColumn(TaskEventVM::getId, "ID");
            eventList.addColumn(TaskEventVM::getTitle, "课程标题");
            eventList.addColumn(TaskEventVM::getDetail, "课程内容");
            initEdit(eventList);
            binder().bind(eventList.adapter(), "taskId");
        }

        void enter(List<TaskEventVM> items) {
            eventList.setItems(items);
        }
    }

    private class ReportWin extends Window {
        private Label report;
        private Map<Integer, TaskAudioVM> audioMap;
        private Map<Integer, TaskVideoVM> videoMap;
        private Map<Integer, TaskStoryVM> storyMap;
        private Map<Integer, TaskClassVM> classMap;
        private Map<Integer, TaskEventVM> eventMap;
        private Map<Integer, List<TaskGroupVM>> groupMap;

        public ReportWin() {
            super("课程报告");
            VerticalLayout layout = new VerticalLayout();
            report = new Label();
            report.setContentMode(ContentMode.HTML);
            layout.addComponent(report);
            layout.setMargin(true);
            setContent(layout);
            setWidthUndefined();
            setModal(true);
            center();
        }

        void enter(List<TaskAudioVM> audioList,
                   List<TaskVideoVM> videoList,
                   List<TaskStoryVM> storyList,
                   List<TaskClassVM> classList,
                   List<TaskEventVM> eventList) {
            audioMap = Maps.uniqueIndex(audioList, TaskAudioVM::getId);
            videoMap = Maps.uniqueIndex(videoList, TaskVideoVM::getId);
            storyMap = Maps.uniqueIndex(storyList, TaskStoryVM::getId);
            classMap = Maps.uniqueIndex(classList, TaskClassVM::getId);
            eventMap = Maps.uniqueIndex(eventList, TaskEventVM::getId);
        }

        void enter(List<CourseWeekVM> weeks, List<TaskGroupVM> groupList) {
            groupMap = groupList.stream().collect(Collectors.groupingBy(TaskGroupVM::getTaskId));
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 7; i++) {
                int weekDay = i + 1;
                sb.append("<b>").append(WEEK[i]).append("</b><hr>");
                for (CourseWeekVM week : weeks) {
                    CourseItemVM item = week.getItem(weekDay);
                    TaskType taskType = item.getTaskType();
                    if (!taskType.equals(TaskType.NONE)) {
                        sb.append(weekDay < 6 ? week.getWorkday() : week.getWeekend()).append("<br>");
                        sb.append("<ul>");
                        switch (taskType) {
                            case LEARN:
                                List<TaskGroupVM> taskList = groupMap.get(item.getTaskId());
                                if (taskList != null) {
                                    for (TaskGroupVM group : taskList) {
                                        sb.append("<li>");
                                        TaskGroup.Type type = group.getType();
                                        sb.append(type.getTitle()).append(": ");
                                        switch (type) {
                                            case AUDIO:
                                                TaskAudioVM audio = audioMap.get(group.getItemId());
                                                sb.append(audio.getTitle()).append(" | ");
                                                sb.append("播放次数: ").append(audio.getTimes()).append(" | ");
                                                sb.append("播放速度: ").append(audio.getSpeed());
                                                break;
                                            case VIDEO:
                                                TaskVideoVM video = videoMap.get(group.getItemId());
                                                sb.append(video.getTitle()).append(" | ");
                                                sb.append("播放次数: ").append(video.getTimes());
                                                break;
                                            case STORY:
                                                TaskStoryVM story = storyMap.get(group.getItemId());
                                                sb.append(story.getTitle()).append(" | ");
                                                sb.append("播放次数: ").append(story.getTimes()).append(" | ");
                                                sb.append("播放速度: ").append(story.getSpeed());
                                                break;
                                        }
                                        sb.append("<br>");
                                        sb.append("</li>");
                                    }
                                }
                                break;
                            case CLASS:
                                TaskClassVM taskClass = classMap.get(item.getTaskId());
                                if (taskClass != null) {
                                    sb.append("<li>");
                                    sb.append("线上教学: ");
                                    sb.append(taskClass.getTitle()).append("<br>");
                                    sb.append("</li>");
                                }
                                break;
                            case EVENT:
                                TaskEventVM taskEvent = eventMap.get(item.getTaskId());
                                if (taskEvent != null) {
                                    sb.append("<li>");
                                    sb.append("线下课程: ");
                                    sb.append(taskEvent.getTitle()).append("<br>");
                                    sb.append("</li>");
                                }
                                break;
                        }
                        sb.append("</ul>");
                        sb.append("<hr>");
                    }
                }
                sb.append("<br>");
            }
            report.setValue(sb.toString());
        }
    }


}
